package script;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;

import main.ErrorException;
import config.ConfigType;
import config.OutputCharset;
import define.Common;
import define.Config;
import define.ErrorDefine;
import define.Keyword;
import excelBase.ExcelFile;
import font.AtlasVar;
import font.Font;

public class Script 
{
	private ExcelFile m_excel;
	public int[] m_scriptCode;
	private int m_scriptLength;
	private Express m_rootExpress;
	private static int[] m_tempArray;
	private static boolean m_isEsc;
	private static boolean m_isComment;
	private ArrayList<Variable> m_globalVariables;
	private ArrayList<StringBuffer> m_receiveVariables;
	private ArrayList<Function> m_functions;
	private ArrayList<Font> m_fonts;
	private ArrayList<AtlasVar> m_atlasList;
	private String m_output;
	private String m_name;
	
	private ArrayList<CommentPosition> m_positions;
	private int m_commentStart;
	private String m_charSet;
	
	public Script(String scriptName, ExcelFile excel, String out)
	{
		m_excel = excel;
		m_output = out;
		m_name = scriptName;
	}

	public void init() throws ErrorException
	{
		m_tempArray = readFile();
		makeScriptCode();
		m_tempArray = null;
		m_globalVariables = new ArrayList<>();
		m_receiveVariables = new ArrayList<>();
		m_functions = new ArrayList<>();
		m_rootExpress = new Express(this, 0, m_scriptLength - 1, null);
		//System.out.println(m_rootExpress.getValue());
	}
	
	public int[] readFile() throws ErrorException
	{
		BufferedReader reader = null;
		try 
		{
			FileInputStream fis = new FileInputStream(m_excel.getPath() + m_name);
			int[] tempArray  = new int[fis.available()];
			int first = (fis.read() & 0xff);
			int second = (fis.read() & 0xff);
			
			if(first == 0xff && second == 0xfe) // unicode
			{
				m_charSet = "UTF-16LE";
			}
			else if(first == 0xfe && second == 0xff) // unicode big endian
			{
				m_charSet = "UTF-16BE";
			}
			else if(first == 0xef && second == 0xbb) // utf 8
			{
				fis.read();
				m_charSet = "UTF-8";
			}
			else //ansic
			{
				fis.close();
				fis = new FileInputStream(m_excel.getPath() + m_name);
				m_charSet = "US-ASCII";
			}
			reader = new BufferedReader(new InputStreamReader(fis, m_charSet));
			int temp = reader.read();
			int i = 0;
			while (temp != -1) {
				tempArray[i] = temp;
				temp = reader.read();
				++i;
			}
			reader.close();
			reader = null;
			return tempArray;
		}
		catch(Exception e) 
		{
			throw new ErrorException(ErrorDefine.E_CANT_READ_SCRIPT_ERROR, m_name, Config.NO_POSITION);
		}
		finally
		{
			try 
			{
				if(reader != null)
				{
					reader.close();
				}
			} 
			catch(Exception e2) 
			{
			}
		}
	}
	
	public Script(StringBuffer codes, ExcelFile excel, ArrayList<Variable> global, VariableContainer father) throws ErrorException
	{
		m_excel = excel;
		m_output = "";
		m_tempArray = new int[codes.length()];
		for(int i = 0; i < m_tempArray.length; ++i)
		{
			m_tempArray[i] = codes.charAt(i);
		}
		makeScriptCode();
		m_tempArray = null;
		m_globalVariables = new ArrayList<>();
		for(Variable var: global)
		{
			m_globalVariables.add(var);
		}
		m_rootExpress = new Express(this, 0, m_scriptLength - 1, father);
	}
	
	private void makeScriptCode()
	{
		m_scriptCode = new int[m_tempArray.length];
		m_isEsc = false;
		m_isComment = false;
		m_scriptLength = 0;
		m_positions = new ArrayList<>();
		for(int i = 0; i < m_tempArray.length; ++i)
		{
			int temp = changeToCode(i);
			if(temp != 0)
			{
				m_scriptCode[m_scriptLength++] = temp;
			}
		}
	}
	
	private int changeToCode(int index)
	{
		if(m_isComment)
		{
			if(m_tempArray[index] == Keyword.E_COMMENT_KEY.getChar())
			{
				int len = index - m_commentStart + 1;
				m_positions.add(new CommentPosition(m_commentStart, len));
				m_isComment = false;
			}
			return 0;
		}
		if(!m_isEsc)
		{
			if(m_tempArray[index] == Keyword.E_COMMENT_KEY.getChar())
			{
				m_isComment = true;
				m_commentStart = index;
				return 0;
			}
			if(m_tempArray[index] == Keyword.E_ESC_KEY.getChar())
			{
				m_isEsc = true;
				return Keyword.E_ESC_KEY.getCode();
			}
		}
		else
		{
			m_isEsc = false;
			return m_tempArray[index];
		}
		Keyword kw = Keyword.isCheck(m_tempArray[index]); 
		if(kw != null)
		{
			return kw.getCode();
		}
		return m_tempArray[index];
	}
	
	public int getLength()
	{
		return m_scriptLength;
	}
	
	public ExcelFile getExcel()
	{
		return m_excel;
	}
	
	public ArrayList<Variable> getGlobalVariables()
	{
		return m_globalVariables;
	}
	
	public void addFunction(Function fun) throws ErrorException
	{
		for(Function temp: m_functions)
		{
			if(temp.getName().compareTo(fun.getName()) == 0)
			{
				throw new ErrorException(ErrorDefine.E_FUNCTION_DUPLICATE_ERROR, temp.getName(), fun.getStart());
			}
		}
		m_functions.add(fun);
	}
	
	public Function getFunction(String name)
	{
		for(Function temp: m_functions)
		{
			if(temp.getName().compareTo(name) == 0)
			{
				return temp;
			}
		}
		return null;
	}
	
	public void setGlobalVariable(Variable var)
	{
		for(int i = 0, max = m_globalVariables.size(); i < max; ++i)
		{
			if(var.getName().compareTo(m_globalVariables.get(i).getName()) == 0)
			{
				m_globalVariables.set(i, var);
				return;
			}
		}
		m_globalVariables.add(var);
	}
	
	public String getName()
	{
		return m_name;
	}
	
	public StringBuffer getValue() throws ErrorException
	{
		clearGlobalList();
		StringBuffer text = m_rootExpress.getValue();
		return text;
	}
	
	public String getOutDir()
	{
		return Common.getPath(Common.m_path + m_output);
	}
	
	public void saveToFile(StringBuffer text) throws ErrorException
	{
		if(m_output.compareTo("") != 0)
		{
			String out = Common.m_path + m_output;
			BufferedWriter writer = null;
			try 
			{
				File file = new File(Common.getPath(out));
				file.mkdirs();
				FileOutputStream fos = new FileOutputStream(out);
				
				writer = new BufferedWriter(new OutputStreamWriter(fos, 
						OutputCharset.getCharset(ConfigType.E_OUT_CHARSET_CT.getStringValue()).getCharsetString()));
				writer.write(text.toString());
				writer.flush();
			} 
			catch(Exception e) 
			{
				throw new ErrorException(ErrorDefine.E_CANT_WRITE_SCRIPT_ERROR, m_name, Config.NO_POSITION);
			}
			finally
			{
				if(writer != null)
				{
					try 
					{
						writer.close();
					}catch(Exception e2) 
					{
					}
				}
			}
		}
	}
	
	public void addReceiveVariable(StringBuffer value)
	{
		m_receiveVariables.add(value);
	}
	
	public ArrayList<StringBuffer> getReceiveList()
	{
		return m_receiveVariables;
	}
	
	public int getRealPosition(int position)
	{
		for(CommentPosition temp: m_positions)
		{
			if(temp.m_position > position)
			{
				break;
			}
			position += temp.m_lenght;
		}
		return position;
	}
	
	public String getCharSet()
	{
		return m_charSet;
	}
	
	public void clearReceiveList()
	{
		m_receiveVariables.clear();
	}
	
	public void clearGlobalList()
	{
		m_globalVariables.clear();
	}
	
	public AtlasVar addAtlas(String name)
	{
		if(m_atlasList == null)
		{
			m_atlasList = new ArrayList<AtlasVar>();
		}
		for(AtlasVar av: m_atlasList)
		{
			if(av.getName().compareTo(name) == 0)
			{
				return av;
			}
		}
		AtlasVar temp = new AtlasVar(name);
		m_atlasList.add(temp);
		return temp;
	}
	
	public Font addFont(String name, String fontName, int size, int position) throws ErrorException
	{
		if(m_fonts == null)
		{
			if(fontName == null)
			{
				throw new ErrorException(ErrorDefine.E_NEW_FONT_NO_FONT_NAME_ERROR, position);
			}
			m_fonts = new ArrayList<Font>();
			m_fonts.add(new Font(name, fontName, size));
			return m_fonts.get(0);
		}
		else
		{
			Font temp = getFont(name);
			if(temp == null)
			{
				if(fontName == null)
				{
					throw new ErrorException(ErrorDefine.E_NEW_FONT_NO_FONT_NAME_ERROR, position);
				}
				temp = new Font(name, fontName, size);
				m_fonts.add(temp);
				return temp;
			}
			return temp;
		}
	}
	
	public Font getFont(String name)
	{
		for(Font temp: m_fonts)
		{
			if(name.compareTo(temp.getName()) == 0)
			{
				return temp;
			}
		}
		return null;
	}
	
	public AtlasVar getAtlas(String name)
	{
		for(AtlasVar temp: m_atlasList)
		{
			if(name.compareTo(temp.getName()) == 0)
			{
				return temp;
			}
		}
		return null;
	}
}
